function TableUI(tableContainer, pagerContainer, caption, height){

    this.model          = new Object();

    this.container      = tableContainer;
    this.pagerContainer = pagerContainer;
    this.caption        = caption;
    this.height         = height;

    this.columns        = new Array();

    this.form           = {};

    this.filterControls = new Array();
    this.pageControls   = new Array();
    this.orderControls  = new Array();

    this.firstButton    = new Object();
    this.prevButton     = new Object();
    this.nextButton     = new Object();
    this.lastButton     = new Object();

    this.regsByPage     = new Object();
    this.pageSelect     = new Object();

    this.onRowClickedListeners = new Array();

    this.setModel = function(model){
        this.model = model;
        this.model.removeOnLoadStartListener(this.reload, this);
        this.model.addOnLoadStartListener(this.reload, this);
    };

    this.initTable = function(rowNum){
        var _this = this;
        if(rowNum == undefined){
            rowNum = 20;
        }
        jQuery("#"+this.container).jqGrid({
            datatype: "json",
            url: _this.model.getFullUrl(),
            mtype: 'POST',
            serializeGridData: function (data){var p = _this.processTableControls(data, _this);return p;},
            height: _this.height,
            colNames: _this.getColumnCaptions(),
            colModel: _this.getColumnModel(),
            rowNum:rowNum,
            rowList:[10,20,30],
            pager: '#'+_this.pagerContainer,
            sortname: this.model.orderColumn.column,
            viewrecords: true,
            sortorder: this.model.order,
            caption: _this.caption,
            onRightClickRow: function(){
                _this.hideColumns();
            },
            onSelectRow: function(id){
                _this.onRowClicked(id, _this)
            },
            loadBeforeSend: function(xhr){
                //xhr.abort();
                xhr.open('POST', _this.model.getFullUrl(), true);
                xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
                xhr.setRequestHeader("Content-length", _this.form.length);
                xhr.setRequestHeader("Connection", "close");
                },
            jsonReader: {
                repeatitems: true,
                id: "id",
                root: function(data){return _this.getRoot(data, _this);},
                page: _this.getPage,
                total: _this.getTotal,
                records: _this.getRecords
            },
            loadComplete: function(data){_this.model.onLoad(data, _this.model);}
        });
        jQuery("#"+this.container).jqGrid('navGrid','#'+this.pagerContainer,{edit:false,add:false,del:false});
    }

    this.addColumn = function (entityName, column, caption, index, width, visible, sortable){
        if(visible == undefined){
            visible = true;
        }

        if(sortable == undefined){
            sortable = true;
        }
        
        var col = this.model.getColumn(entityName, column);

        var formatter = function(cellValue, options, rowObject){
            cellValue = new String(cellValue);
            if(cellValue == "null"){
                return "";
            }
            return cellValue;
        };

        if(col != undefined){
            if(col.type == "date"){
                formatter = function(cellValue, options, rowObject){
                    cellValue = new String(cellValue);
                    return formatDate(cellValue.substr(0, 10));
                };
            }
        }

        var searchOptions = {
            sopt: ['eq']
        };

        if(col != undefined){
            if(col.type == "date"){
                searchOptions.dataInit = function(elem){
                    jQuery(elem).datepicker(getDatePickerOptions());
                };
            }
        }


        this.columns[this.columns.length] = {
            entityName: entityName,
            column: column,
            caption: caption,
            index: index,
            width: width,
            hidden: !visible,
            formatter: formatter,
            searchoptions: searchOptions,
            sortable: sortable
        };
    }
    
    this.getColumnCaptions = function(){
        var captions = new Array();
        for(var i=0; i<this.columns.length; i++){
            var col = this.columns[i];
            captions[i] = col.caption;
        }
        return captions;
    }

    this.getColumnModel = function(){
        var colModel = new Array();
        for(var i=0; i<this.columns.length; i++){
            var col = this.columns[i];
            colModel[i] = {
                name: col.entityName+"-"+col.column,
                index: col.index,
                width: col.width,
                hidden: col.hidden,
                formatter: col.formatter,
                searchoptions: col.searchoptions,
                sortable: col.sortable
            };
        }
        return colModel;
    }

    this.hideColumns = function(){
        jQuery("#"+this.container).setColumns({
            resize: false,
            width: 400,
            beforeShowForm: function(id){
                $(id).parent().css("height", "300");
                $(id).parent().css("overflow", "auto");
            }
        });
    }

    this.getColumns = function(){
        return this.columns;
    }

    this.getColumnByName = function(colName){
        for(var i=0; i<this.columns.length; i++){
            var col = this.columns[i];
            if(col.column == colName){
                return col;
            }
        }
        return undefined;
    }

    this.processTableControls = function(postData, context){
        context.model.setLimit(postData.rows);
        context.model.setPage(postData.page - 1);

        var orderCol = context.getColumnByName(postData.sidx);
        context.model.setOrder(orderCol.entityName, orderCol.column, postData.sord);

        context.model.clearUserFilter();
        if(postData._search){
            var filterCol = context.getColumnByName(postData.searchField);
            context.model.setUserFilter(filterCol.entityName, filterCol.column, postData.searchString);
        }

        var params = context.model.getFilterParameters();
        context.form = params;
        return params;
        //return JSON.stringify(params);
    }

    this.getRoot = function(data, context){
       var root = new Array();

       var cols = context.getColumns();

       for(var i=0;i<data.data.length;i++){
           var rowData = new Array();
           for(var j=0; j<cols.length; j++){
               rowData[j] = data.data[i][cols[j].entityName][cols[j].column];
           }
           root[root.length] = {
                id: data.data[i][context.model.idColumn.entityName][context.model.idColumn.column],
                cell: rowData
           };
       }

       context.model.onLoad(data, context.model);

       return root;
    }

    this.getPage = function(data){
        return data.pageInfo.page;
    }

    this.getTotal = function(data){
        return Math.ceil(Number(data.pageInfo.total) / Number(data.pageInfo.limit));
    }

    this.getRecords = function(data){
        return data.pageInfo.total;
    }

    this.reload  = function(context){
        var _this = this;
        if(context != undefined){
            _this = context;
        }
       $("#"+_this.container).jqGrid().trigger("reloadGrid");
    }

    this.showRows = function(data, model, context){

        jQuery("#"+context.container).jqGrid('clearGridData');

        var cols = context.getColumns();
        var page = Number(data.pageInfo.page);
        var total = Number(data.pageInfo.total);
        var limit = Number(data.pageInfo.limit);

        var start = (page - Number(1)) * limit;
        var end = start + limit; 

       for(var i=0;i<total;i++){
           if(i>=start && i<end){
               var rowData = new Object();
               for(var j=0; j<cols.length; j++){
                   rowData[cols[j].column] = data.data[i-start][cols[j].entityName][cols[j].column];
               }
               jQuery("#"+context.container).jqGrid(
                   'addRowData',
                   data.data[i-start][context.model.idColumn.entityName][context.model.idColumn.column],
                   rowData
               );
           }else{
               var rowData = new Object();
               for(var j=0; j<cols.length; j++){
                   rowData[cols[j].column] = "";
               }
               jQuery("#"+context.container).jqGrid(
                   'addRowData',
                   i,
                   rowData
               );
           }
       }

        /*
         * for(var j=0; j<cols.length; j++){
                    rowData[cols[j].column] = "";
                }
                jQuery("#"+context.container).jqGrid(
                    'addRowData',
                    i*(-1),
                    rowData
                );
         */

        jQuery("#"+context.container).jqGrid('hideCol','id');

        jQuery("#"+context.container).jqGrid('setGridParam',
            {
                page: data.pageInfo.page
            }
        ).trigger("reloadGrid");
    };

    this.addFilterControl = function(entityName, column, control){

        if(this.model.getColumn(entityName, column).type == "date"){
            control = showDate(control);
        }

        this.filterControls[this.filterControls.length] = {
                                                          entityName: entityName,
                                                          column: column,
                                                          control: control};
        var _this = this;
        $(control).change(function(){
            _this.filterChanged(this, _this);
        });
    };

    this.filterChanged = function(control, context){
        for(var i=0; i<context.filterControls.length; i++){
            currControl = context.filterControls[i];
            if(currControl.control == control){
                context.model.setFilter(currControl.entityName, currControl.column, $(control).val());
                context.model.load();
            }
        }
    }

    this.addPageControl = function(control){
        this.pageControls[this.pageControls.length] = control;
    };

    this.addOrderControl = function(entityName, column, control){
        this.orderControls[this.orderControls.length] = {entityName: entityName, 
                                                         column: column, 
                                                         control: control};

        if(entityName == this.model.orderColumn.entityName &&
           column == this.model.orderColumn.column){

           if(this.model.order == "asc"){
               $(control).attr("ascDesc", "asc");
               $(control).attr("src", webRoot+"img/menu/arrow_down.gif");
           }else{
               $(control).attr("ascDesc", "desc");
               $(control).attr("src", webRoot+"img/menu/arrow_up.gif");
           }

        }else{
            $(control).removeAttr("ascDesc");
            $(control).attr("src", webRoot+"img/menu/arrow.gif");
        }

        var _this = this;
        $(control).click(function(){_this.orderClicked(this, _this)});
    };

    this.orderClicked = function(control, context){
        if($(control).attr("ascDesc") == undefined || $(control).attr("ascDesc") == "desc"){
            $(control).attr("ascDesc", "asc");
            $(control).attr("src", webRoot+"img/menu/arrow_down.gif");
        }else if($(control).attr("ascDesc") == "asc"){
            $(control).attr("ascDesc", "desc");
            $(control).attr("src", webRoot+"img/menu/arrow_up.gif");
        }

        for(var i=0; i<context.orderControls.length; i++){
            var currControl = context.orderControls[i];
            if(currControl.control != control){
               $(currControl.control).removeAttr("ascDesc");
               $(currControl.control).attr("src", webRoot+"img/menu/arrow.gif");
            }else{
                context.model.setOrder(
                    currControl.entityName,
                    currControl.column,
                    $(currControl.control).attr("ascDesc")
                );

                context.model.load();
            }
        }
    }

    this.setFirstButton    = function(control){
        this.firstButton = control;

        var _this = this;
        $(control).click(function(){
            _this.model.first();
            _this.model.load();
        });
    };
    
    this.setPrevButton     = function(control){
        this.prevButton = control;
        var _this = this;
        $(control).click(function(){
            _this.model.prev();
            _this.model.load();
        });
    };
    
    this.setNextButton     = function(control){
        this.nextButton = control;

        var _this = this;
        $(control).click(function(){
            _this.model.next();
            _this.model.load();
        });
    };
    
    this.setLastButton     = function(control){
        this.lastButton = control;

        var _this = this;
        $(control).click(function(){
            _this.model.last();
            _this.model.load();
        });
    };

    this.navigatePages     = function(pgButton, context){
        switch (pgButton){
            case 'first':
                context.model.first();
                break;
            case 'prev':
               context.model.prev();
               break;
            case 'next':
                context.model.next();
                break;
            case 'last':
                context.model.last();
                break;
            case 'records':
                var limit = jQuery("#"+context.container).jqGrid('getGridParam', 'rowNum');
                context.model.setLimit(limit);
                break;
        }
        jQuery("#"+context.container).jqGrid('setGridParam',{url:context.model.getFullUrl()}).trigger("reloadGrid");
    }

    this.setRegsByPageControl = function(control){
        this.regsByPage = control;

        var _this = this;
        $(control).change(function(){
            _this.model.setLimit($(this).val());
            $(_this.pageSelect).val(0);
            $(_this.pageSelect).change();
            _this.model.load();
        });
    };

    this.setPagesControl = function(control){
        this.pageSelect = control;

        var _this = this;
        $(control).change(function(){
            _this.model.setPage($(this).val());
            _this.model.load();
        });
    };

    this.updatePages = function(model, context){
        $(context.pageSelect).html("");
        for(var i=0; i<model.cantPages(); i++){
            var op = "<option value='"+i+"'>"+Number(i+1)+"</option>";
            $(context.pageSelect).append(op);
        }
    }

    this.onRowClicked = function (id, context){
        context.model.setEntityId(id);
        for(var i=0; i<context.onRowClickedListeners.length; i++){
            context.onRowClickedListeners[i].callback(id, context, context.onRowClickedListeners[i].context);
        }
    };

    this.addOnonRowClickedListener = function(func, context){
        this.onRowClickedListeners[this.onRowClickedListeners.length] = {callback: func, context:context};
    };
}


